Skip to content

Immer

One of the cardinal rules in React is that state is immutable. This is true whether we're talking about useState or useReducer.

The more complex our state is, the trickier it is to figure out how to make the necessary changes without mutating anything.

Let's suppose we're building a calendar app, and we have an array of events. It might look something like this:

const [events, setEvents] = React.useState([
{
eventId: 'coffee-with-samantha',
date: '2023-01-01T12:30:00.000Z',
metadata: {
invitees: [
{
name: 'Samantha',
email: 'samboombox123@aol.com',
},
],
},
},
{
eventId: 'focus-time',
date: '2023-01-01T15:00:00.000Z',
metadata: {
notes: 'Time for me to focus!',
},
},
{
eventId: 'team-meeting',
date: '2023-01-02T10:00:00.000Z',
metadata: {
notes: 'Weekly team catch-up call!',
invitees: [
{
name: 'Sadb Fabian',
email: 'sfabian@widgetco.com',
},
{
name: 'Gerarda Nicomedes',
email: 'gnicomedes@widgetco.com',
},
{
name: 'Sagit Edvaldo',
email: 'sedvaldo@widgetco.com',
},
{
name: 'Denis Seppo',
email: 'dseppo@widgetco.com',
},
],
},
},
]);

Now, let's suppose we want to remove Gerarda Nicomedes from the team meeting. How do we do that without mutating any of the arrays or objects held in React state?

I've set up a little playground for you to try and solve this. If you'd like, you can spend a few minutes trying to solve this problem.

The playground is like a mini test framework. It'll show you the expected result alongside the actual value you return. When you've successfully completed the problem, a "✅" will appear in the top right corner.

Code Playground

/*
Your mission is to update the `team-meeting`
calendar event so that the second teammate,
Gerarda Nicomedes, is removed from the list
of invitees. You are not allowed to mutate
any arrays or objects.
PRO-TIP: full-screen and resize the “Result” pane
to see both objects side-by-side, for easier
debugging.
*/

function updateState(currentState) {
// Do your work here.
return currentState;
}

export default updateState;

If you're curious about how this playground actually works, in terms of the validation, you can open it in CodeSandbox to view all files. Click the icon in the top-right corner of the playground.

Phew!

So, I have a lot of experience doing this sort of thing; in the early days of Redux, a lot of our state looked like this! And even with that experience, I still found this to be a pretty thorny problem.

The fact that we're not allowed to mutate anything makes this problem much harder than it should be. If only we could mutate the state, we could solve this problem in 1 line of code:

function updateState(currentState) {
currentState[2].metadata.invitees.splice(1, 1);
return currentState;
}

(Admittedly, the splice method isn't exactly user-friendly either. But overall, this solution requires much less head-scratching!)

What if there was a way to solve things this way without actually mutating the underlying data?

That's where Immer comes in. 😄